#include <iostream>
#include <unistd.h>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <memory>

using TaskFun = std::function<void()>;
using ReleaseFun = std::function<void()>;

// 设计的一个任务类，是任务轮要执行的任务
class Task
{
public:
    Task(uint64_t id, uint32_t timeout, TaskFun func)
        : _id(id), _timeout(timeout), _tfunc(func), _iscancel(false)
    {
    }

    void SetReleaseFun(ReleaseFun rfunc)
    {
        _rfunc = rfunc;
    }

    // 根据定时轮的实现逻辑可以知道任务的执行在对象析构时
    ~Task()
    {
        if (_iscancel == false)
        {
            _tfunc();
        }

        _rfunc();
    }

    uint32_t Gettimeout()
    {
        return _timeout;
    }

    void SetCancel()
    {
        _iscancel = true;
    }

private:
    // 唯一的一个用户编号，要在外部统一设置进来，不可由执行流设置
    uint64_t _id;
    uint32_t _timeout; // 任务的超时时间(也是刷新延迟时间)

    // 对象要执行的任务
    TaskFun _tfunc;
    ReleaseFun _rfunc;

    // 是否取消相关任务
    bool _iscancel;
};

class TimeWheel
{
private:
    void Remove(uint64_t id)
    {
        auto pos = _mmap.find(id);
        if (pos == _mmap.end())
        {
            std::cerr << "找不到对应的任务对象！" << std::endl;
            return;
        }

        // 找到了
        _mmap.erase(pos);
    }

public:
// 如果std::vector<std::vector<std::shared_ptr<Task>>>类型，但没有指定大小。
// 当程序尝试访问_wheel的索引时（例如在AddTimer、RefreshTimer和Run方法中），
// 如果索引超出当前向量大小，就会导致内存访问越界，从而引发段错误。
    TimeWheel(int tick = 0, int capacity = 60)
        : _tick(tick), _capactiy(capacity),_wheel(60)
    {
    }

    // 向哈希表中增加一个新的weak_ptr对象 + 轮子里面增加shared_ptr管理的Task对象
    void AddTimerInLoop(uint64_t id, uint32_t delay, const TaskFun &cb)
    {
        // std::cout<<"---------"<<std::endl; for debug
        // step1. 构建shared_ptr对象，并设置相关函数
        std::shared_ptr<Task> task = std::make_shared<Task>(id, delay, cb);
        // std::cout<<"---------"<<std::endl; for debug
        task->SetReleaseFun(std::bind(&TimeWheel::Remove, this, id)); // 注意不使用bind会导致参数不匹配问题

        // std::cout<<"---------"<<std::endl; for debug
        // 添加到管理部分
        _mmap[id] = std::weak_ptr<Task>(task);

        // std::cout<<"---------"<<std::endl; for debug
        int pos = (_tick + delay) % _capactiy;
        // 添加到执行部分
        _wheel[pos].emplace_back(task);
        // std::cout<<"---------"<<std::endl; for debug
    }

    // 轮子里面增加shared_ptr管理的Task对象
    void RefreshTimer(uint64_t id)
    {
        auto it = _mmap.find(id);
        if (it == _mmap.end())
        {
            std::cerr << "位置不存在!" << std::endl;
            return;
        }

        // 找到了
        // 添加到执行部分
        std::shared_ptr<Task> task = it->second.lock();
        int pos = (_tick + task->Gettimeout()) % _capactiy;
        _wheel[pos].push_back(task);
    }

    // 秒针每秒向后走一次
    void Run()
    {
        _tick = (_tick + 1) % _capactiy;
        _wheel[_tick].clear(); // 释放数组中的对象
    }

    // 取消定时任务方法：不能直接在数组中释放掉管理的对象，这并不会取消而是提前执行
    void CancelTimer(uint64_t id)
    {
        auto it = _mmap.find(id);
        if (it == _mmap.end())
        {
            std::cerr << "位置不存在!" << std::endl;
            return;
        }

        // 找到了
        // 添加到执行部分
        std::shared_ptr<Task> task = it->second.lock();
        task->SetCancel();
    }

private:
    // 执行部分-->里面放着的是被管理的Task对象
    std::vector<std::vector<std::shared_ptr<Task>>> _wheel;

    // 管理部分
    // 1.weak_ptr解决了shared_ptr不能减到0的问题
    // 2.new_qus：当weak_ptr指向的对象的引用计数为0之后要将改智能指针对象从_mmap中删除，防止资源泄露
    //   solution：因为是Task对象在析构时要删除，那么在Task类中增加一个回调函数在析构时从_mmap中删除
    std::unordered_map<uint64_t, std::weak_ptr<Task>> _mmap;

    int _tick;     // 指针的位置，指针在哪里，哪里的智能指针对象就要被释放，当引用计数为0的时候Task对象被析构执行任务
    int _capactiy; // 秒数(数组的大小 | _tick的最大移动范围)
};

class Test
{
public:
    Test() { std::cout << "构造" << std::endl; }
    ~Test() { std::cout << "析构" << std::endl; }
};

void DelTest(Test *t)
{
    delete t;
}

int main()
{
    TimeWheel tw;
    Test *t = new Test();

    std::cout << "-------" << std::endl;
    tw.AddTimerInLoop(888, 5, std::bind(DelTest, t));

    std::cout << "-------" << std::endl;
    for (int i = 0; i < 5; i++)
    {
        std::cout << "刷新一下定时任务,5s后将要被执行" << std::endl;
        tw.RefreshTimer(888);
        tw.Run();
        sleep(1);
    }

    tw.CancelTimer(888);
    while (true)
    {
        tw.Run();
        sleep(1);
    }

    return 0;
}